Skip to main content

7: Verify Issued Credential

Sending proof request

Once the connection is established, the Verifier can send a proof request.

There are optional restrictions and additional fields that can be added to the proof request, which are beyond the scope of this simple example. For more information, please see our docs for restrictions on proofs.

curl -X 'POST' \
'https://cloudapi.test.didxtech.com/tenant/v1/verifier/send-request' \
-H 'accept: application/json' \
-H 'Content-Type: application/json' \
-d '{
"protocol_version": "v2",
"comment": "Demo",
"type": "indy",
"indy_proof_request": {
"requested_attributes": {
"holder_surname": { "name": "Surname", "restrictions":[]},
"holder_name": { "name": "Name", "restrictions": []},
"holder_age": { "name": "Age", "restrictions": []}
},
"requested_predicates": {}
},
"connection_id": "5ef9f4e0-9f98-4e43-aef7-de11da2ccd40"
}'

Response:

{
"connection_id": "5ef9f4e0-9f98-4e43-aef7-de11da2ccd40",
"created_at": "2023-11-22T10:12:20.755226Z",
"error_msg": null,
"parent_thread_id": "ce5b5597-d3fa-437b-a857-0927694cc4b9",
"presentation": null,
"presentation_request": {
"name": null,
"non_revoked": null,
"nonce": "1040329690360437135931695",
"requested_attributes": {
"holder_surname": {
"name": "Surname",
"names": null,
"non_revoked": null,
"restrictions": []
},
"holder_name": {
"name": "Name",
"names": null,
"non_revoked": null,
"restrictions": []
},
"holder_age": {
"name": "Age",
"names": null,
"non_revoked": null,
"restrictions": []
}
},
"requested_predicates": {},
"version": null
},
"proof_id": "v2-57c1bf16-1fc3-4506-b672-8b11580c4920",
"protocol_version": "v2",
"role": "verifier",
"state": "request-sent",
"thread_id": "ce5b5597-d3fa-437b-a857-0927694cc4b9",
"updated_at": "2023-11-22T10:12:20.755226Z",
"verified": null
}

Note that the verifier will now have what's called a presentation exchange record in state: request-sent. Pending presentation records can be viewed by calling GET /v1/verifier/proofs, and completed presentation exchange records are deleted by default, but can be preserved by adding an optional save_exchange_record=True field to the request.

Holder responds to proof request

The holder would have received a webhook event on topic proofs, indicating they have received a request. Example webhook:

{
"wallet_id": "4e0c70fb-f2ad-4f59-81f3-93d8df9b977a",
"topic": "proofs",
"origin": "multitenant",
"payload": {
"connection_id": "ab1cc0fe-d797-429c-be36-7830a79d52a1",
"created_at": "2023-11-16T09:59:19.612647Z",
"error_msg": null,
"parent_thread_id": null,
"presentation": null,
"presentation_request": null,
"proof_id": "v2-ba39fb0f-4dff-4bce-8db0-fdad3432cc7d",
"protocol_version": "v2",
"role": "prover",
"state": "request-received",
"thread_id": "aea706fd-5492-4ed7-ab1c-1bb9ff309926",
"updated_at": "2023-11-16T09:59:19.612647Z",
"verified": null
}
}

The Holder will now see a presentation exchange record when they call GET on the /v1/verifier/proofs endpoint:

curl -X 'GET' \
'https://cloudapi.test.didxtech.com/tenant/v1/verifier/proofs' \
-H 'accept: application/json'
-H 'x-api-key: tenant.<holder token>' \

Response:

[
{
"connection_id": "ab1cc0fe-d797-429c-be36-7830a79d52a1",
"created_at": "2023-11-16T09:59:19.612647Z",
"error_msg": null,
"parent_thread_id": "aea706fd-5492-4ed7-ab1c-1bb9ff309926",
"presentation": null,
"presentation_request": {
"name": "Proof Request",
"non_revoked": null,
"nonce": "234234",
"requested_attributes": {
"holder_surname": {
"name": "surname",
"names": null,
"non_revoked": null,
"restrictions": []
},
"holder_name": {
"name": "name",
"names": null,
"non_revoked": null,
"restrictions": []
},
"holder_age": {
"name": "age",
"names": null,
"non_revoked": null,
"restrictions": []
}
},
"requested_predicates": {},
"version": "1.0"
},
"proof_id": "v2-ba39fb0f-4dff-4bce-8db0-fdad3432cc7d",
"protocol_version": "v2",
"role": "prover",
"state": "request-received",
"thread_id": "aea706fd-5492-4ed7-ab1c-1bb9ff309926",
"updated_at": "2023-11-16T09:59:19.612647Z",
"verified": null
}
]

Note that their role indicates prover, and the state is request-received. Prover is the term used for a holder in a proof exchange. Additionally, note that the prover and the verifier have different proof_id references for the same proof interaction.

The Holder/Prover can now check which credentials match the fields that are requested in the proof request by using the proof_id and making a call to /v1/verifier/proofs/{proof_id}/credentials.

NOTE: If the call is successful, but returns an empty list [], it means that the credentials of the Holder do not match the requested fields in the proof request.

curl -X 'GET' \
'https://cloudapi.test.didxtech.com/tenant/v1/verifier/proofs/v2-93e29a31-5eab-4091-9d1d-f27220f445fd/credentials' \
-H 'accept: application/json'

Response:

NOTE: This response is a list. Each object in this list corresponds to a credential, matching the requested attributes. In this case, the response has only one object, meaning all the requested attributes are found in one credential.

[
{
"cred_info": {
"attrs": {
"Age": "25",
"Surname": "Holder",
"Name": "Alice"
},
"cred_def_id": "2hPti9M3aQqsRCy8N6jrDB:3:CL:10:Demo Person",
"cred_rev_id": null,
"referent": "10e6b03f-2b60-431a-9634-731594423120",
"rev_reg_id": null,
"schema_id": "QpSW24YVf61A3sAWxArfF6:2:Person:0.1.0"
},
"interval": null,
"presentation_referents": ["holder_name", "holder_age", "holder_surname"]
}
]

We can now use the referent (the referent is the holder's reference to their credential id) from the response above to accept the proof request:

curl -X 'POST' \
'https://cloudapi.test.didxtech.com/tenant/v1/verifier/accept-request' \
-H 'accept: application/json' \
-H 'Content-Type: application/json' \
-d '{
"proof_id": "v2-614a1035-c855-417d-8a8e-0c824bb6ab0f",
"type": "indy",
"indy_presentation_spec": {
"requested_attributes": {
"holder_surname": {
"cred_id": "10e6b03f-2b60-431a-9634-731594423120",
"revealed": true
},
"holder_name": {
"cred_id": "10e6b03f-2b60-431a-9634-731594423120",
"revealed": true
},
"holder_age": {
"cred_id": "10e6b03f-2b60-431a-9634-731594423120",
"revealed": true
}
},
"requested_predicates": {},
"self_attested_attributes": {},
},
"dif_presentation_spec": {}
}'
Click to see Response
{
"connection_id": "43264326-57a7-4ef4-aa65-906dd9c15961",
"created_at": "2023-11-22T06:11:46.956062Z",
"error_msg": null,
"parent_thread_id": "13a9375e-c124-445d-9242-d77ee54cf47f",
"presentation": {
"identifiers": [
{
"cred_def_id": "2hPti9M3aQqsRCy8N6jrDB:3:CL:10:Demo Person",
"rev_reg_id": null,
"schema_id": "QpSW24YVf61A3sAWxArfF6:2:Person:0.1.0",
"timestamp": null
}
],
"proof": {
"aggregated_proof": {
"c_hash": "3219322627487542487718476352817657516298296911958347169452423884069641575597",
"c_list": [
[2,221,130,248,96,145,3,19,86,111,75,202,101,190,166,101,222,83,181,133,88,134,...]
]
},
"proofs": [
{
"non_revoc_proof": null,
"primary_proof": {
"eq_proof": {
"a_prime": "92597261364394573165798933206533873274818813554675314388834609214520451243506734698448...",
"e": "13957611453314484376536548594867291175484947973045451726862617289480834912062823268784462725...",
"m": {
"master_secret": "113138788366524810935384910670667169176189186849867816774981002913273272990310..."
},
"m2": "9527966448141582634179217567781606687146704440935821032302777460114542256740868407661366941...",
"revealed_attrs": {
"holder_age": "25",
"holder_name": "27034640024117331033063128044004318218486816931520886405535659934417438781507",
"holder_surname": "108415864455171922802944099373800995974825385451497756533671241088029831060565"
},
"v": "13310269669535827858183383537992882823597862340798892194315939604086055386082234517559247559..."
},
"ge_proofs": []
}
}
]
},
"requested_proof": {
"predicates": {},
"revealed_attr_groups": null,
"revealed_attrs": {
"holder_surname": {
"encoded": "108415864455171922802944099373800995974825385451497756533671241088029831060565",
"raw": "Holder",
"sub_proof_index": 0
},
"holder_name": {
"encoded": "27034640024117331033063128044004318218486816931520886405535659934417438781507",
"raw": "Alice",
"sub_proof_index": 0
},
"holder_age": {
"encoded": "25",
"raw": "25",
"sub_proof_index": 0
}
},
"self_attested_attrs": {},
"unrevealed_attrs": {}
}
},
"presentation_request": {
"name": "Proof Request",
"non_revoked": null,
"nonce": "234234",
"requested_attributes": {
"holder_surname": {
"name": "surname",
"names": null,
"non_revoked": null,
"restrictions": []
},
"holder_name": {
"name": "name",
"names": null,
"non_revoked": null,
"restrictions": []
},
"holder_age": {
"name": "age",
"names": null,
"non_revoked": null,
"restrictions": []
}
},
"requested_predicates": {},
"version": "1.0"
},
"proof_id": "v2-614a1035-c855-417d-8a8e-0c824bb6ab0f",
"protocol_version": "v2",
"role": "prover",
"state": "presentation-sent",
"thread_id": "13a9375e-c124-445d-9242-d77ee54cf47f",
"updated_at": "2023-11-22T06:28:01.553809Z",
"verified": null
}

If the proof request is valid, then the verification will complete automatically. Once again, we wait for the exchange to be completed by listening to webhooks. Here is an example webhook event for the topic proofs in the done state.

{
"wallet_id": "92893658-fe2d-4f2d-8268-a60a601945a9",
"topic": "proofs",
"origin": "multitenant",
"payload": {
"connection_id": "5ef9f4e0-9f98-4e43-aef7-de11da2ccd40",
"created_at": "2023-11-22T06:11:46.897536Z",
"error_msg": null,
"parent_thread_id": null,
"presentation": null,
"presentation_request": null,
"proof_id": "v2-e83d3d75-9eb1-4d54-a321-7d0d5c5d286e",
"protocol_version": "v2",
"role": "verifier",
"state": "done",
"thread_id": "13a9375e-c124-445d-9242-d77ee54cf47f",
"updated_at": "2023-11-22T06:28:01.704464Z",
"verified": true
}
}

Verifier can get the proof with all its data by making the following call:

curl -X 'GET' \
'https://cloudapi.test.didxtech.com/tenant/v1/verifier/proofs' \
-H 'accept: application/json'
Click to see Response
[
{
"connection_id": "5ef9f4e0-9f98-4e43-aef7-de11da2ccd40",
"created_at": "2023-11-22T06:11:46.897536Z",
"error_msg": null,
"parent_thread_id": "13a9375e-c124-445d-9242-d77ee54cf47f",
"presentation": {
"identifiers": [
{
"cred_def_id": "2hPti9M3aQqsRCy8N6jrDB:3:CL:10:Demo Person",
"rev_reg_id": null,
"schema_id": "QpSW24YVf61A3sAWxArfF6:2:Person:0.1.0",
"timestamp": null
}
],
"proof": {
"aggregated_proof": {
"c_hash": "3219322627487542487718476352817657516298296911958347169452423884069641575597",
"c_list": [
[2,221,130,248,96,145,3,19,86,111,75,202,101,190,166,101,222,83,181,133,88,134,152,205,154,157,...]
]
},
"proofs": [
{
"non_revoc_proof": null,
"primary_proof": {
"eq_proof": {
"a_prime": "9259726136439457316579893320653387327481881355467531438883460921452045124350673469620...",
"e": "1395761145331448437653654859486729117548494797304545172686261728948083491206282326878446272...",
"m": {
"master_secret": "11313878836652481093538491067066716917618918684986781677498100291327327294712..."
},
"m2": "952796644814158263417921756778160668714670444093582103230277746011454225674086840766136694...",
"revealed_attrs": {
"holder_age": "25",
"holder_name": "27034640024117331033063128044004318218486816931520886405535659934417438781507",
"holder_surname": "108415864455171922802944099373800995974825385451497756533671241088029831060565"
},
"v": "1331026966953582785818338353799288282359786234079889219431593960408605538608223451755924755..."
},
"ge_proofs": []
}
}
]
},
"requested_proof": {
"predicates": {},
"revealed_attr_groups": null,
"revealed_attrs": {
"holder_surname": {
"encoded": "108415864455171922802944099373800995974825385451497756533671241088029831060565",
"raw": "Holder",
"sub_proof_index": 0
},
"holder_name": {
"encoded": "27034640024117331033063128044004318218486816931520886405535659934417438781507",
"raw": "Alice",
"sub_proof_index": 0
},
"holder_age": {
"encoded": "25",
"raw": "25",
"sub_proof_index": 0
}
},
"self_attested_attrs": {},
"unrevealed_attrs": {}
}
},
"presentation_request": {
"name": "Proof Request",
"non_revoked": null,
"nonce": "234234",
"requested_attributes": {
"holder_surname": {
"name": "surname",
"names": null,
"non_revoked": null,
"restrictions": []
},
"holder_name": {
"name": "name",
"names": null,
"non_revoked": null,
"restrictions": []
},
"holder_age": {
"name": "age",
"names": null,
"non_revoked": null,
"restrictions": []
}
},
"requested_predicates": {},
"version": "1.0"
},
"proof_id": "v2-e83d3d75-9eb1-4d54-a321-7d0d5c5d286e",
"protocol_version": "v2",
"role": "verifier",
"state": "done",
"thread_id": "13a9375e-c124-445d-9242-d77ee54cf47f",
"updated_at": "2023-11-22T06:28:01.704464Z",
"verified": true
}
]

Hooray! 🥳🎉 Well done, you now know how to issue and verify credentials!

If you would like to learn about revoking credentials, please proceed to the next section: Revoking Credentials.